#include <avr\io.h>
#include <stdio.h>
#include "Serial.h"
#include "Common.h"


void serial_init( int32_t baudrate, Serial *port )
{
	int16_t bdiv = (FOSC/16+baudrate/2)/baudrate - 1;
	int16_t bdiv2 = (FOSC/8+baudrate/2)/baudrate - 1;
	int8_t dbl;
	int32_t berror = baudrate - (bdiv + 1)*16*FOSC;
	int32_t berror2 = baudrate - (bdiv2 + 1)*8*FOSC;
	if( berror < 0 ) { berror = -berror; }
	if( berror2 < 0 ) { berror2 = -berror2; }
	if( berror2 < berror )
	{
		dbl = 1;
		bdiv = bdiv2;
	}
	else
	{
		dbl = 0;
	}

	switch( port->port )
	{
		case 0:
		UBRR0 = bdiv;
		UCSR0A = (1 << TXC0) | (dbl << U2X0);
		UCSR0B = (1 << RXEN0) |
		(1 << TXEN0);
		UCSR0C = (1 << UCSZ01) |
		(1 << UCSZ00);
		DDRE |= 0b00000010;
		break;
		case 1:
		UBRR1 = bdiv;
		UCSR1A = (1 << TXC1) | (dbl << U2X1);
		UCSR1B = (1 << RXEN1) |
		(1 << TXEN1);
		UCSR1C = (1 << UCSZ11) |
		(1 << UCSZ10);
		DDRD |= 0b00001000;
		break;
		case 2:
		UBRR2 = bdiv;
		UCSR2A = (1 << TXC2) | (dbl << U2X2);
		UCSR2B = (1 << RXEN2) |
		(1 << TXEN2);
		UCSR2C = (1 << UCSZ21) |
		(1 << UCSZ20);
		DDRH |= 0b00000010;
		break;
		case 3:
		UBRR3 = bdiv;
		UCSR0A = (1 << TXC3) | (dbl << U2X3);
		UCSR3B = (1 << RXEN3) |
		(1 << TXEN3);
		UCSR3C = (1 << UCSZ31) |
		(1 << UCSZ30);
		DDRJ |= 0b00000010;
		break;
	}
}

int serial_putonechar( char c, Serial *port )
{
	if( c == '\n' )
	{
		serial_putonechar( '\r', port );
	}

	switch (port->port)
	{
		case 0:
		while(( UCSR0A & (1 << UDRE0)) == 0)
		{
		}
		UDR0 = c;
		break;

		case 1:
		while(( UCSR1A & (1 << UDRE1)) == 0)
		{
		}
		UDR1 = c;
		break;

		case 2:
		while(( UCSR2A & (1 << UDRE2)) == 0)
		{
		}
		UDR2 = c;
		break;

		case 3:
		while(( UCSR3A & (1 << UDRE3)) == 0)
		{
		}
		UDR3 = c;
		break;
	}
	
	return 0;
}

int serial_readonechar( Serial *port )
{
	while( serial_available( port ) == 0);
	uint8_t ret = (port->port == 0) ? UDR0 :
	(port->port == 1) ? UDR1 :
	(port->port == 2) ? UDR2 :
	(port->port == 3) ? UDR3 :
	-1;
	serial_putonechar( ret, port );
	if( ret == '\r' )
	{
		ret = '\n';
		serial_putonechar( ret, port );
	}
	
	return ret;
}

int serial_available( Serial *port )
{
	uint8_t v = (port->port == 0) ? UCSR0A & (1 << RXC0) :
	(port->port == 1) ? UCSR1A & (1 << RXC1) :
	(port->port == 2) ? UCSR2A & (1 << RXC2) :
	(port->port == 3) ? UCSR3A & (1 << RXC3) :
	0xff;
	return v != 0 ? 1 : 0;
}

int serial_available_f( FILE *f )
{
	return serial_available( fdev_get_udata( f) );
}
